home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / xulrunner / python / xpcom / client / __init__.py
Encoding:
Python Source  |  2008-01-10  |  21.2 KB  |  479 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python XPCOM language bindings.
  15. #
  16. # The Initial Developer of the Original Code is
  17. # ActiveState Tool Corp.
  18. # Portions created by the Initial Developer are Copyright (C) 2000, 2001
  19. # the Initial Developer. All Rights Reserved.
  20. #
  21. # Contributor(s):
  22. #   Mark Hammond <MarkH@ActiveState.com> (original author)
  23. #
  24. # Alternatively, the contents of this file may be used under the terms of
  25. # either the GNU General Public License Version 2 or later (the "GPL"), or
  26. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. # in which case the provisions of the GPL or the LGPL are applicable instead
  28. # of those above. If you wish to allow use of your version of this file only
  29. # under the terms of either the GPL or the LGPL, and not to allow others to
  30. # use your version of this file under the terms of the MPL, indicate your
  31. # decision by deleting the provisions above and replace them with the notice
  32. # and other provisions required by the GPL or the LGPL. If you do not delete
  33. # the provisions above, a recipient may use your version of this file under
  34. # the terms of any one of the MPL, the GPL or the LGPL.
  35. #
  36. # ***** END LICENSE BLOCK *****
  37.  
  38. import os
  39. import new
  40. from xpcom import xpt, COMException, nsError
  41.  
  42. # Suck in stuff from _xpcom we use regularly to prevent a module lookup
  43. from xpcom._xpcom import IID_nsISupports, IID_nsIClassInfo, IID_nsISupportsCString, IID_nsISupportsWeakReference, \
  44.         IID_nsIWeakReference, XPTI_GetInterfaceInfoManager, GetComponentManager, XPTC_InvokeByIndex
  45.  
  46. # Attribute names we may be __getattr__'d for, but know we don't want to delegate
  47. # Could maybe just look for startswith("__") but this may screw things for some objects.
  48. _special_getattr_names = ["__del__", "__len__", "__nonzero__"]
  49.  
  50. _just_int_interfaces = ["nsISupportsPRInt32", "nsISupportsPRInt16", "nsISupportsPRUint32", "nsISupportsPRUint16", "nsISupportsPRUint8", "nsISupportsPRBool"]
  51. _just_long_interfaces = ["nsISupportsPRInt64", "nsISupportsPRUint64"]
  52. _just_float_interfaces = ["nsISupportsDouble", "nsISupportsFloat"]
  53. # When doing a specific conversion, the order we try the interfaces in.
  54. _int_interfaces = _just_int_interfaces + _just_float_interfaces
  55. _long_interfaces = _just_long_interfaces + _just_int_interfaces + _just_float_interfaces
  56. _float_interfaces = _just_float_interfaces + _just_long_interfaces + _just_int_interfaces
  57.  
  58. method_template = """
  59. def %s(self, %s):
  60.     return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
  61. """
  62. def _MakeMethodCode(method):
  63.     # Build a declaration
  64.     param_no = 0
  65.     param_decls = []
  66.     param_flags = []
  67.     param_names = []
  68.     used_default = 0
  69.     for param in method.params:
  70.         param_no = param_no + 1
  71.         param_name = "Param%d" % (param_no,)
  72.         param_default = ""
  73.         if not param.hidden_indicator and param.IsIn() and not param.IsDipper():
  74.             if param.IsOut() or used_default: # If the param is "inout", provide a useful default for the "in" direction.
  75.                 param_default = " = None"
  76.                 used_default = 1 # Once we have used one once, we must for the rest!
  77.             param_decls.append(param_name + param_default)
  78.             param_names.append(param_name)
  79.  
  80.         type_repr = xpt.MakeReprForInvoke(param)
  81.         param_flags.append( (param.param_flags,) +  type_repr )
  82.     sep = ", "
  83.     param_decls = sep.join(param_decls)
  84.     if len(param_names)==1: # Damn tuple reprs.
  85.         param_names = param_names[0] + ","
  86.     else:
  87.         param_names = sep.join(param_names)
  88.     # A couple of extra newlines make them easier to read for debugging :-)
  89.     return method_template % (method.name, param_decls, method.method_index, tuple(param_flags), param_names)
  90.  
  91. # Keyed by IID, each item is a tuple of (methods, getters, setters)
  92. interface_cache = {}
  93. # Keyed by [iid][name], each item is an unbound method.
  94. interface_method_cache = {}
  95.  
  96. # Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
  97. contractid_info_cache = {}
  98. have_shutdown = 0
  99.  
  100. def _shutdown():
  101.     interface_cache.clear()
  102.     interface_method_cache.clear()
  103.     contractid_info_cache.clear()
  104.     global have_shutdown
  105.     have_shutdown = 1
  106.  
  107. # Fully process the named method, generating method code etc.
  108. def BuildMethod(method_info, iid):
  109.     name = method_info.name
  110.     try:
  111.         return interface_method_cache[iid][name]
  112.     except KeyError:
  113.         pass
  114.     # Generate it.
  115.     assert not (method_info.IsSetter() or method_info.IsGetter()), "getters and setters should have been weeded out by now"
  116.     method_code = _MakeMethodCode(method_info)
  117.     # Build the method - We only build a function object here
  118.     # - they are bound to each instance as needed.
  119.     
  120. ##    print "Method Code for %s (%s):" % (name, iid)
  121. ##    print method_code
  122.     codeObject = compile(method_code, "<XPCOMObject method '%s'>" % (name,), "exec")
  123.     # Exec the code object
  124.     tempNameSpace = {}
  125.     exec codeObject in globals(), tempNameSpace
  126.     ret = tempNameSpace[name]
  127.     if not interface_method_cache.has_key(iid):
  128.         interface_method_cache[iid] = {}
  129.     interface_method_cache[iid][name] = ret
  130.     return ret
  131.  
  132. from xpcom.xpcom_consts import XPT_MD_GETTER, XPT_MD_SETTER, XPT_MD_NOTXPCOM, XPT_MD_CTOR, XPT_MD_HIDDEN
  133. FLAGS_TO_IGNORE = XPT_MD_NOTXPCOM | XPT_MD_CTOR | XPT_MD_HIDDEN
  134.  
  135. # Pre-process the interface - generate a list of methods, constants etc,
  136. # but don't actually generate the method code.
  137. def BuildInterfaceInfo(iid):
  138.     assert not have_shutdown, "Can't build interface info after a shutdown"
  139.     ret = interface_cache.get(iid, None)
  140.     if ret is None:
  141.         # Build the data for the cache.
  142.         method_code_blocks = []
  143.         getters = {}
  144.         setters = {}
  145.         method_infos = {}
  146.         
  147.         interface = xpt.Interface(iid)
  148.         for m in interface.methods:
  149.             flags = m.flags
  150.             if flags & FLAGS_TO_IGNORE == 0:
  151.                 if flags & XPT_MD_SETTER:
  152.                     param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
  153.                     setters[m.name] = m.method_index, param_flags
  154.                 elif flags & XPT_MD_GETTER:
  155.                     param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
  156.                     getters[m.name] = m.method_index, param_flags
  157.                 else:
  158.                     method_infos[m.name] = m
  159.  
  160.         # Build the constants.
  161.         constants = {}
  162.         for c in interface.constants:
  163.             constants[c.name] = c.value
  164.         ret = method_infos, getters, setters, constants
  165.         interface_cache[iid] = ret
  166.     return ret
  167.  
  168. class _XPCOMBase:
  169.     def __cmp__(self, other):
  170.         try:
  171.             other = other._comobj_
  172.         except AttributeError:
  173.             pass
  174.         return cmp(self._comobj_, other)
  175.  
  176.     def __hash__(self):
  177.         return hash(self._comobj_)
  178.  
  179.     # See if the object support strings.
  180.     def __str__(self):
  181.         try:
  182.             self._comobj_.QueryInterface(IID_nsISupportsCString, 0)
  183.             return str(self._comobj_)
  184.         except COMException:
  185.             return self.__repr__()
  186.  
  187.     # Try the numeric support.
  188.     def _do_conversion(self, interface_names, cvt):
  189.         iim = XPTI_GetInterfaceInfoManager()
  190.         for interface_name in interface_names:
  191.             iid = iim.GetInfoForName(interface_name).GetIID()
  192.             try:
  193.                 prim = self._comobj_.QueryInterface(iid)
  194.                 return cvt(prim.data)
  195.             except COMException:
  196.                 pass
  197.         raise ValueError, "This object does not support automatic numeric conversion to this type"
  198.  
  199.     def __int__(self):
  200.         return self._do_conversion(_int_interfaces, int)
  201.  
  202.     def __long__(self):
  203.         return self._do_conversion(_long_interfaces, long)
  204.  
  205.     def __float__(self):
  206.         return self._do_conversion(_float_interfaces, float)
  207.     
  208. class Component(_XPCOMBase):
  209.     def __init__(self, ob, iid):
  210.         assert not hasattr(ob, "_comobj_"), "Should be a raw nsIWhatever, not a wrapped one"
  211.         ob_name = None
  212.         if not hasattr(ob, "IID"):
  213.             ob_name = ob
  214.             cm = GetComponentManager()
  215.             ob = cm.createInstanceByContractID(ob)
  216.             assert not hasattr(ob, "_comobj_"), "The created object should be a raw nsIWhatever, not a wrapped one"
  217.         # Keep a reference to the object in the component too
  218.         self.__dict__['_comobj_'] = ob
  219.         # hit __dict__ directly to avoid __setattr__()
  220.         self.__dict__['_interfaces_'] = {} # keyed by IID
  221.         self.__dict__['_interface_names_'] = {} # keyed by IID name
  222.         self.__dict__['_interface_infos_'] = {} # keyed by IID
  223.         self.__dict__['_name_to_interface_name_'] = {}
  224.         self.__dict__['_tried_classinfo_'] = 0
  225.  
  226.         if ob_name is None:
  227.             ob_name = "<unknown>"
  228.         self.__dict__['_object_name_'] = ob_name
  229.         self.QueryInterface(iid)
  230.  
  231.     def _build_all_supported_interfaces_(self):
  232.         # Use nsIClassInfo, but don't do it at object construction to keep perf up.
  233.         # Only pay the penalty when we really need it.
  234.         assert not self._tried_classinfo_, "already tried to get the class info."
  235.         self.__dict__['_tried_classinfo_'] = 1
  236.         # See if nsIClassInfo is supported.
  237.         try:
  238.             classinfo = self._comobj_.QueryInterface(IID_nsIClassInfo, 0)
  239.         except COMException:
  240.             classinfo = None
  241.         if classinfo is not None:
  242.             try:
  243.                 real_cid = classinfo.contractID
  244.             except COMException:
  245.                 real_cid = None
  246.             if real_cid:
  247.                 self.__dict__['_object_name_'] = real_cid
  248.                 contractid_info = contractid_info_cache.get(real_cid)
  249.             else:
  250.                 contractid_info = None
  251.             if contractid_info is None:
  252.                 try:
  253.                     interface_infos = classinfo.getInterfaces()
  254.                 except COMException:
  255.                     interface_infos = []
  256.                 for nominated_iid in interface_infos:
  257.                     # Interface may appear twice in the class info list, so check this here.
  258.                     if not self.__dict__['_interface_infos_'].has_key(nominated_iid):
  259.                         self._remember_interface_info(nominated_iid)
  260.                 if real_cid is not None:
  261.                     contractid_info = {}
  262.                     contractid_info['_name_to_interface_name_'] = self.__dict__['_name_to_interface_name_']
  263.                     contractid_info['_interface_infos_'] = self.__dict__['_interface_infos_']
  264.                     contractid_info_cache[real_cid] = contractid_info
  265.             else:
  266.                 for key, val in contractid_info.items():
  267.                     self.__dict__[key].update(val)
  268.  
  269.         self.__dict__['_com_classinfo_'] = classinfo
  270.  
  271.     def _remember_interface_info(self, iid):
  272.         method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
  273.         # Remember all the names so we can delegate
  274.         assert not self.__dict__['_interface_infos_'].has_key(iid), "Already remembered this interface!"
  275.         self.__dict__['_interface_infos_'][iid] = method_infos, getters, setters, constants
  276.         interface_name = iid.name
  277.         names = self.__dict__['_name_to_interface_name_']
  278.         for name in method_infos.keys(): names[name] = interface_name
  279.         for name in getters.keys(): names[name] = interface_name
  280.         for name in setters.keys(): names[name] = interface_name
  281.         for name in constants.keys():  names[name] = interface_name
  282.  
  283.     def _make_interface_info(self, ob, iid):
  284.         interface_infos = self._interface_infos_
  285.         assert not self._interfaces_.has_key(iid), "Already have made this interface"
  286.         method_infos, getters, setters, constants = interface_infos[iid]
  287.         new_interface = _Interface(ob, iid, method_infos, getters, setters, constants)
  288.         self._interfaces_[iid] = new_interface
  289.         self._interface_names_[iid.name] = new_interface
  290.         # No point remembering these.
  291.         del interface_infos[iid]
  292.  
  293.     def QueryInterface(self, iid):
  294.         if self._interfaces_.has_key(iid):
  295.             assert self._interface_names_.has_key(iid.name), "_interfaces_ has the key, but _interface_names_ does not!"
  296.             return self
  297.         # Haven't seen this before - do a real QI.
  298.         if not self._interface_infos_.has_key(iid):
  299.             self._remember_interface_info(iid)
  300.         ret = self._comobj_.QueryInterface(iid, 0)
  301. #        print "Component QI for", iid, "yielded", ret
  302.         self._make_interface_info(ret, iid)
  303.         assert self._interfaces_.has_key(iid) and self._interface_names_.has_key(iid.name), "Making the interface didn't update the maps"
  304.         return self
  305.  
  306.     queryInterface = QueryInterface # Alternate name.
  307.  
  308.     def __getattr__(self, attr):
  309.         if attr in _special_getattr_names:
  310.             raise AttributeError, attr
  311.         # First allow the interface name to return the "raw" interface
  312.         interface = self.__dict__['_interface_names_'].get(attr, None)
  313.         if interface is not None:
  314.             return interface
  315.         interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
  316.         # This may be first time trying this interface - get the nsIClassInfo
  317.         if interface_name is None and not self._tried_classinfo_:
  318.             self._build_all_supported_interfaces_()
  319.             interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
  320.             
  321.         if interface_name is not None:
  322.             interface = self.__dict__['_interface_names_'].get(interface_name, None)
  323.             if interface is None:
  324.                 iid = XPTI_GetInterfaceInfoManager().GetInfoForName(interface_name).GetIID()
  325.                 self.QueryInterface(iid)
  326.                 interface = self.__dict__['_interface_names_'][interface_name]
  327.             return getattr(interface, attr)
  328.         # Some interfaces may provide this name via "native" support.
  329.         # Loop over all interfaces, and if found, cache it for next time.
  330.         for interface in self.__dict__['_interfaces_'].values():
  331.             try:
  332.                 ret = getattr(interface, attr)
  333.                 self.__dict__['_name_to_interface_name_'][attr] = interface._iid_.name
  334.                 return ret
  335.             except AttributeError:
  336.                 pass
  337.         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
  338.         
  339.     def __setattr__(self, attr, val):
  340.         interface_name = self._name_to_interface_name_.get(attr, None)
  341.         # This may be first time trying this interface - get the nsIClassInfo
  342.         if interface_name is None and not self._tried_classinfo_:
  343.             self._build_all_supported_interfaces_()
  344.             interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
  345.         if interface_name is not None:
  346.             interface = self._interface_names_.get(interface_name, None)
  347.             if interface is None:
  348.                 iid = XPTI_GetInterfaceInfoManager().GetInfoForName(interface_name).GetIID()
  349.                 self.QueryInterface(iid)
  350.                 interface = self.__dict__['_interface_names_'][interface_name]
  351.             setattr(interface, attr, val)
  352.             return
  353.         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
  354.     def __repr__(self):
  355.         # We can advantage from nsIClassInfo - use it.
  356.         try:
  357.             if not self._tried_classinfo_:
  358.                 self._build_all_supported_interfaces_()
  359.             assert self._tried_classinfo_, "Should have tried the class info by now!"
  360.         except COMException:
  361.             # Error building the info - ignore the error, but ensure that
  362.             # we are flagged as *not* having built, so the error is seen
  363.             # by the first caller who actually *needs* this to work.
  364.             self.__dict__['_tried_classinfo_'] = 0
  365.         infos = self.__dict__['_interface_infos_']
  366.         if infos:
  367.             iface_names = ",".join([iid.name for iid in infos.keys()])
  368.             iface_desc = " (implementing %s)" % (iface_names,)
  369.         else:
  370.             iface_desc = " (with no class info)"
  371.         return "<XPCOM component '%s'%s>" % (self._object_name_,iface_desc)
  372.  
  373. class _Interface(_XPCOMBase):
  374.     def __init__(self, comobj, iid, method_infos, getters, setters, constants):
  375.         self.__dict__['_comobj_'] = comobj
  376.         self.__dict__['_iid_'] = iid
  377.         self.__dict__['_property_getters_'] = getters
  378.         self.__dict__['_property_setters_'] = setters
  379.         self.__dict__['_method_infos_'] = method_infos # method infos waiting to be turned into real methods.
  380.         self.__dict__['_methods_'] = {} # unbound methods
  381.         self.__dict__['_object_name_'] = iid.name
  382.         self.__dict__.update(constants)
  383.         # We remember the constant names to prevent the user trying to assign to them!
  384.         self.__dict__['_constant_names_'] = constants.keys()
  385.  
  386.     def __getattr__(self, attr):
  387.         # Allow the underlying interface to provide a better implementation if desired.
  388.         if attr in _special_getattr_names:
  389.             raise AttributeError, attr
  390.  
  391.         ret = getattr(self.__dict__['_comobj_'], attr, None)
  392.         if ret is not None:
  393.             return ret
  394.         # Do the function thing first.
  395.         unbound_method = self.__dict__['_methods_'].get(attr, None)
  396.         if unbound_method is not None:
  397.             return new.instancemethod(unbound_method, self, self.__class__)
  398.  
  399.         getters = self.__dict__['_property_getters_']
  400.         info = getters.get(attr)
  401.         if info is not None:
  402.             method_index, param_infos = info
  403.             if len(param_infos)!=1: # Only expecting a retval
  404.                 raise RuntimeError, "Can't get properties with this many args!"
  405.             args = ( param_infos, () )
  406.             return XPTC_InvokeByIndex(self, method_index, args)
  407.  
  408.         # See if we have a method info waiting to be turned into a method.
  409.         # Do this last as it is a one-off hit.
  410.         method_info = self.__dict__['_method_infos_'].get(attr, None)
  411.         if method_info is not None:
  412.             unbound_method = BuildMethod(method_info, self._iid_)
  413.             # Cache it locally
  414.             self.__dict__['_methods_'][attr] = unbound_method
  415.             return new.instancemethod(unbound_method, self, self.__class__)
  416.  
  417.         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
  418.  
  419.     def __setattr__(self, attr, val):
  420.         # If we already have a __dict__ item of that name, and its not one of
  421.         # our constants, we just directly set it, and leave.
  422.         if self.__dict__.has_key(attr) and attr not in self.__dict__['_constant_names_']:
  423.             self.__dict__[attr] = val
  424.             return
  425.         # Start sniffing for what sort of attribute this might be?
  426.         setters = self.__dict__['_property_setters_']
  427.         info = setters.get(attr)
  428.         if info is None:
  429.             raise AttributeError, "XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr)
  430.         method_index, param_infos = info
  431.         if len(param_infos)!=1: # Only expecting a single input val
  432.             raise RuntimeError, "Can't set properties with this many args!"
  433.         real_param_infos = ( param_infos, (val,) )
  434.         return XPTC_InvokeByIndex(self, method_index, real_param_infos)
  435.  
  436.     def __repr__(self):
  437.         return "<XPCOM interface '%s'>" % (self._object_name_,)
  438.  
  439.  
  440. # Called by the _xpcom C++ framework to wrap interfaces up just
  441. # before they are returned.
  442. def MakeInterfaceResult(ob, iid):
  443.     return Component(ob, iid)
  444.  
  445. class WeakReference:
  446.     """A weak-reference object.  You construct a weak reference by passing
  447.     any COM object you like.  If the object does not support weak
  448.     refs, you will get a standard NS_NOINTERFACE exception.
  449.  
  450.     Once you have a weak-reference, you can "call" the object to get
  451.     back a strong reference.  Eg:
  452.  
  453.     >>> some_ob = components.classes['...']
  454.     >>> weak_ref = WeakReference(some_ob)
  455.     >>> new_ob = weak_ref() # new_ob is effectively "some_ob" at this point
  456.     >>> # EXCEPT: new_ob may be None of some_ob has already died - a
  457.     >>> # weak reference does not keep the object alive (that is the point)
  458.  
  459.     You should never hold onto this resulting strong object for a long time,
  460.     or else you defeat the purpose of the weak-reference.
  461.     """
  462.     def __init__(self, ob, iid = None):
  463.         swr = Component(ob._comobj_, IID_nsISupportsWeakReference)
  464.         self._comobj_ = Component(swr.GetWeakReference()._comobj_, IID_nsIWeakReference)
  465.         if iid is None:
  466.             try:
  467.                 iid = ob.IID
  468.             except AttributeError:
  469.                 iid = IID_nsISupports
  470.         self._iid_ = iid
  471.     def __call__(self, iid = None):
  472.         if iid is None: iid = self._iid_
  473.         try:
  474.             return Component(self._comobj_.QueryReferent(iid)._comobj_, iid)
  475.         except COMException, details:
  476.             if details.errno != nsError.NS_ERROR_NULL_POINTER:
  477.                 raise
  478.             return None
  479.